package info.niwota.fwrite.imglist;

import info.niwota.fwrite.Debug;
import info.niwota.fwrite.FileItem;
import info.niwota.fwrite.R;
import info.niwota.fwrite.StatusMonitor;

import java.util.ArrayList;
import java.util.HashSet;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author qiangli
 * 
 */
public class MultiSelectImageActivity extends Activity implements
		GridViewSpecial.Listener, GridViewSpecial.DrawAdapter {
	private class LoadTask extends AsyncTask<Void, Void, Integer> {
		private static final int WHAT_FAIL = 2;
		private static final int WHAT_OK = 1;

		private Throwable error;

		public LoadTask() {
		}

		@Override
		protected Integer doInBackground(Void... params) {
			Integer rc;
			try {
				load();
				rc = (WHAT_OK);
			} catch (Throwable e) {
				rc = (WHAT_FAIL);
				error = e;
			} finally {
				isLoading = false;
			}
			return rc;
		}

		@Override
		protected void onCancelled() {
			isLoading = false;
			try {
				dismissDialog(DIALOG_PROGRESS);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		@Override
		protected void onPostExecute(Integer result) {
			try {
				dismissDialog(DIALOG_PROGRESS);
			} catch (Exception e) {
			}
			try {
				refreshView();
			} catch (Throwable e) {
				e.printStackTrace();
			}
			if (result == null) {
				return;
			}
			switch (result) {
			case WHAT_OK: {
				break;
			}
			case WHAT_FAIL: {
				showError(error);
				break;
			}
			}
		}

		@Override
		protected void onPreExecute() {
			showDialog(DIALOG_PROGRESS);
		}
	}

	private static final int DIALOG_PROGRESS = 1;

	public static final String EXTRA_URIS = "extra.uris";

	private static final float INVALID_POSITION = -1f;

	private static final String STATE_SCROLL_POSITION = "scroll_position";

	private static final String STATE_SELECTED_INDEX = "first_index";

	private static final String TAG = "MultiSelectImageActivity";

	private static final int RC_VIEW = 1;

	private FileList mAllImages;

	private boolean mConfigurationChanged = false;

	private final Rect mDstRect = new Rect();;

	private Animation mFooterAppear;

	private Animation mFooterDisappear;

	private View mFooterOrganizeView;

	private GridViewSpecial mGvs;

	private final Handler mHandler = new Handler();

	private boolean mLayoutComplete;

	private ImageLoader mLoader;

	private Bitmap mMissingImageThumbnailBitmap;

	private HashSet<FileItem> mMultiSelected = null;

	private boolean isMultiSelect = false;

	private Drawable mMultiSelectFalse;

	private Drawable mMultiSelectTrue;

	private View mNoImagesView;

	private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);

	private boolean mPausing = true;

	private BroadcastReceiver mReceiver = null;

	private float mScrollPosition = INVALID_POSITION;

	// The index of the first picture in GridViewSpecial.
	private int mSelectedIndex = GridViewSpecial.INDEX_NONE;

	// mSrcRect and mDstRect are only used in drawImage, but we put them as
	// instance variables to reduce the memory allocation overhead because
	// drawImage() is called a lot.
	private final Rect mSrcRect = new Rect();

	private ProgressDialog progressDialog;

	private Handler progressHandler = new Handler() {

		public void handleMessage(Message msg) {
			CharSequence m = statusMonitor.message;
			if (Debug.DEBUG) {
				Log.d(TAG, "message: " + m);
			}
			progressDialog.setTitle(m);
		}
	};

	private StatusMonitor statusMonitor = new StatusMonitor();

	private boolean isLoading;

	private Button btnOk;

	private Button btnClose;

	boolean canHandleEvent() {
		// Don't process event in pause state.
		return (!mPausing) && (mLayoutComplete);
	}

	private void closeMultiSelectMode() {
		if (mMultiSelected == null) {
			return;
		}
		mMultiSelected = null;
		mGvs.invalidate();
		// hideFooter();
	}

	private ProgressDialog createProgressDialog() {
		final Context ctx = this;
		ProgressDialog pd = new ProgressDialog(ctx);
		Uri uri = getIntent().getData();
		pd.setTitle(uri.getPath());
		pd.setMessage(ctx.getText(R.string.wait));
		pd.setIndeterminate(true);
		pd.setCancelable(false); //can't cancel
		DialogInterface.OnClickListener l = new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				statusMonitor.aborted = true;
			}
		};
		pd.setButton(ProgressDialog.BUTTON_POSITIVE,
				getString(R.string.action_cancel), l);
		return pd;
	}

	private void doCancel() {
		closeMultiSelectMode();
		//
		setResult(RESULT_CANCELED);
		finish();
	}

	private void doFinish() {
		ArrayList<Uri> list = new ArrayList<Uri>();
		// for (IImage image : mMultiSelected) {
		// Uri u = image.fullSizeImageUri();
		// list.add(u);
		// }
		// for (FileItem i : mMultiSelected) {
		// Uri u = image.fullSizeImageUri();
		//			
		// list.add(u);
		// }
		//
		Uri base = getIntent().getData();
		if (Debug.DEBUG) {
			Log.d(TAG, "base uri: " + base);
		}

		for (FileItem i : mMultiSelected) {
			// String p = i.getName();
			final Uri u = Uri.parse("file:" + i.getName());

			if (Debug.DEBUG) {
				Log.d(TAG, "selected uri: " + u);
			}
			list.add(u);
		}
		//
		closeMultiSelectMode();

		Intent data = new Intent();
		// data.putExtra(EXTRA_URIS, list);
		data.putParcelableArrayListExtra(EXTRA_URIS, list);
		setResult(RESULT_OK, data);
		finish();
	}

	public void drawDecoration(Canvas canvas, Object image, int xPos, int yPos,
			int w, int h) {
		if (mMultiSelected != null) {
			initializeMultiSelectDrawables();

			Drawable checkBox = mMultiSelected.contains(image) ? mMultiSelectTrue
					: mMultiSelectFalse;
			int width = checkBox.getIntrinsicWidth();
			int height = checkBox.getIntrinsicHeight();
			int left = 5 + xPos;
			int top = h - height - 5 + yPos;
			mSrcRect.set(left, top, left + width, top + height);
			checkBox.setBounds(mSrcRect);
			checkBox.draw(canvas);
		}
	}

	public void drawImage(Canvas canvas, IImage image, Bitmap b, int xPos,
			int yPos, int w, int h) {
		if (b != null) {
			// if the image is close to the target size then crop,
			// otherwise scale both the bitmap and the view should be
			// square but I suppose that could change in the future.

			int bw = b.getWidth();
			int bh = b.getHeight();

			int deltaW = bw - w;
			int deltaH = bh - h;

			if (deltaW >= 0 && deltaW < 10 && deltaH >= 0 && deltaH < 10) {
				int halfDeltaW = deltaW / 2;
				int halfDeltaH = deltaH / 2;
				mSrcRect.set(0 + halfDeltaW, 0 + halfDeltaH, bw - halfDeltaW,
						bh - halfDeltaH);
				mDstRect.set(xPos, yPos, xPos + w, yPos + h);
				canvas.drawBitmap(b, mSrcRect, mDstRect, null);
			} else {
				mSrcRect.set(0, 0, bw, bh);
				mDstRect.set(xPos, yPos, xPos + w, yPos + h);
				canvas.drawBitmap(b, mSrcRect, mDstRect, mPaint);
			}
		} else {
			// If the thumbnail cannot be drawn, put up an error icon
			// instead
			Bitmap error = getErrorBitmap(image);
			int width = error.getWidth();
			int height = error.getHeight();
			mSrcRect.set(0, 0, width, height);
			int left = (w - width) / 2 + xPos;
			int top = (w - height) / 2 + yPos;
			mDstRect.set(left, top, left + width, top + height);
			canvas.drawBitmap(error, mSrcRect, mDstRect, null);
		}
	}

	public Bitmap getErrorBitmap(IImage image) {
		// Create this bitmap lazily, and only once for all the ImageBlocks to
		// use
		if (mMissingImageThumbnailBitmap == null) {
			mMissingImageThumbnailBitmap = BitmapFactory.decodeResource(
					getResources(), R.drawable.ic_missing_thumbnail_picture);
		}
		return mMissingImageThumbnailBitmap;
	}

	private void hideFooter() {
		if (mFooterOrganizeView.getVisibility() != View.GONE) {
			mFooterOrganizeView.setVisibility(View.GONE);
			if (mFooterDisappear == null) {
				mFooterDisappear = AnimationUtils.loadAnimation(this,
						R.anim.footer_disappear);
			}
			mFooterOrganizeView.startAnimation(mFooterDisappear);
		}
	}

	private void initializeFooterButtons() {
		btnOk = (Button) findViewById(R.id.button_ok);
		btnOk.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				doFinish();
			}
		});
		btnOk.setEnabled(false);

		btnClose = (Button) findViewById(R.id.button_close);
		btnClose.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				doCancel();
			}
		});
		btnClose.setEnabled(true);
	}

	private void initializeMultiSelectDrawables() {
		if (mMultiSelectTrue == null) {
			mMultiSelectTrue = getResources().getDrawable(
					R.drawable.btn_check_buttonless_on);
		}
		if (mMultiSelectFalse == null) {
			mMultiSelectFalse = getResources().getDrawable(
					R.drawable.btn_check_buttonless_off);
		}
	}

	private void initLoad() {
		isLoading = true;
		//
		LoadTask task = new LoadTask();
		task.execute();
	}

	private boolean isInMultiSelectMode() {
		return isMultiSelect; // (mMultiSelected != null);
	}

	private void load() {
		try {
			Uri uri = getIntent().getData();
			if (Debug.DEBUG) {
				Log.d(TAG, "uri: " + uri);
			}
			statusMonitor.aborted = false;
			statusMonitor.handler = progressHandler;
			//
			if (mAllImages == null) {
				mAllImages = new FileList();
			}
			mAllImages.load(this, uri, statusMonitor);
			//
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public boolean needsDecoration() {
		// return (mMultiSelected != null);
		return isMultiSelect;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (Debug.DEBUG) {
			Log.d(TAG, "requestCode: " + requestCode + " resultCode: "
					+ resultCode + " data: " + data);
		}
		if (requestCode == RC_VIEW) {
			return;
		}
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		mConfigurationChanged = true;
	}

	protected void onCreate(Bundle savedInstanceState) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreate savedInstanceState: " + savedInstanceState);
		}
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

		setContentView(R.layout.image_gallery);

		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
				R.layout.custom_gallery_title);

		mNoImagesView = findViewById(R.id.no_images);

		mGvs = (GridViewSpecial) findViewById(R.id.grid);
		mGvs.setListener(this);
		mGvs.invalidate();

		mFooterOrganizeView = findViewById(R.id.footer_organize);

		// consume all click events on the footer view
		mFooterOrganizeView.setOnClickListener(Util.getNullOnClickListener());

		initializeFooterButtons();

		mLoader = new ImageLoader(getContentResolver(), mHandler);
		//
		String action = getIntent().getAction();
		isMultiSelect = (action != null && (action.equals(Intent.ACTION_PICK) || action
				.equals(Intent.ACTION_GET_CONTENT)));
		if (isMultiSelect) {
			openMultiSelectMode();
			showFooter();
		} else {
			hideFooter();
		}
		//
		initTitle();
		
		if (Debug.DEBUG) {
			Log.d(TAG, "end onCreate ");
		}
	}

	private void initTitle() {
		try {
			String title = getIntent().getStringExtra("windowTitle");
			title = (TextUtils.isEmpty(title) ? getString(R.string.application_name) : title);
			setCustomTitle(title);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_PROGRESS:
			progressDialog = createProgressDialog();
			return progressDialog;
		default:
		}
		return null;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		return false;
	}

	public void onImageClicked(int index) {
		if (index < 0 || index >= mAllImages.getCount()) {
			return;
		}
		mSelectedIndex = index;
		mGvs.setSelectedIndex(index);

		// IImage image = mAllImages.getImageAt(index);
		FileItem fi = mAllImages.getItem(index);
		if (isInMultiSelectMode()) {
			toggleMultiSelected(fi);
			return;
		} else {
			showItem(fi);
		}
	}

	public void onImageTapped(int index) {
		// In the multiselect mode, once the finger finishes tapping, we hide
		// the selection box by setting the selected index to none. However, if
		// we use the dpad center key, we will keep the selected index in order
		// to show the the selection box. We do this because we have the
		// multiselect marker on the images to indicate which of them are
		// selected, so we don't need the selection box, but in the dpad case
		// we still need the selection box to show as a "cursor".

		if (isInMultiSelectMode()) {
			mGvs.setSelectedIndex(GridViewSpecial.INDEX_NONE);
			toggleMultiSelected(mAllImages.getItem(index));
			// toggleMultiSelected(mAllImages.getImageAt(index));
		} else {
			onImageClicked(index);
		}
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (!canHandleEvent()) {
			return false;
		}
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			if (mMultiSelected == null || mMultiSelected.size() == 0) {
				doCancel();
				return true;
			}
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	public void onLayoutComplete(boolean changed) {
		mLayoutComplete = true;

		mGvs.setSelectedIndex(mSelectedIndex);
		if (mScrollPosition == INVALID_POSITION) {
			mGvs.scrollToImage(0);
		} else if (mConfigurationChanged) {
			mConfigurationChanged = false;
			mGvs.scrollTo(mScrollPosition);
			if (mGvs.getCurrentSelection() != GridViewSpecial.INDEX_NONE) {
				mGvs.scrollToVisible(mSelectedIndex);
			}
		} else {
			mGvs.scrollTo(mScrollPosition);
		}
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		return false;
	}

	@Override
	public void onPause() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onPause");
		}
		super.onPause();

		mPausing = true;

		mLoader.stop();

		mGvs.stop();

		if (mReceiver != null) {
			unregisterReceiver(mReceiver);
			mReceiver = null;
		}

		// Now that we've paused the threads that are using the cursor it is
		// safe to close it.

		mAllImages.close();
		mAllImages = null;
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		return true;
	}

	@Override
	protected void onRestoreInstanceState(Bundle state) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onRestoreInstanceState: " + state);
		}
		super.onRestoreInstanceState(state);
		mScrollPosition = state.getFloat(STATE_SCROLL_POSITION,
				INVALID_POSITION);
		mSelectedIndex = state.getInt(STATE_SELECTED_INDEX, 0);
	}

	@Override
	public void onResume() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onResume isLoading: " + isLoading);
		}
		super.onResume();
		//
		// mGvs.setSizeChoice(Integer.parseInt(mPrefs.getString(
		// "pref_gallery_size_key", "1")));

		// mGvs.requestFocus();

		mPausing = false;

		// install an intent filter to receive SD card related events.
		IntentFilter intentFilter = new IntentFilter(
				Intent.ACTION_MEDIA_MOUNTED);
		intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
		intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
		intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
		intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);
		intentFilter.addDataScheme("file");

		mReceiver = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				String action = intent.getAction();
				if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
					// SD card available
					// put up a "please wait" message
					// also listen for the media scanner finished message
				} else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
					// SD card unavailable
					// rebake(true, false);
				} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
					// rebake(false, true);
				} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
					// rebake(false, false);
				} else if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
					// rebake(true, false);
				}
			}
		};
		registerReceiver(mReceiver, intentFilter);
		//
		rebake();
	}

	@Override
	protected void onSaveInstanceState(Bundle state) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onSaveInstanceState: " + state);
		}
		super.onSaveInstanceState(state);
		//
		state.putFloat(STATE_SCROLL_POSITION, mScrollPosition);
		state.putInt(STATE_SELECTED_INDEX, mSelectedIndex);
	}

	public void onScroll(float scrollPosition) {
		mScrollPosition = scrollPosition;
	}

	private void openMultiSelectMode() {
		if (mMultiSelected != null) {
			return;
		}
		mMultiSelected = new HashSet<FileItem>();
		mGvs.invalidate();
	}

	private void refreshView() {
		try {
			if (isLoading) {
				return;
			}
			if (mAllImages == null) {
				return;
			}
			mLoader.stop();
			mGvs.stop();
			//
			mGvs.setImageList(mAllImages);
			mGvs.setDrawAdapter(this);
			mGvs.setLoader(mLoader);
			//
			mGvs.start();
			mNoImagesView.setVisibility(mAllImages.getCount() > 0 ? View.GONE
					: View.VISIBLE);
			//
			if (isMultiSelect) {
				int sc = (mMultiSelected == null ? 0 : mMultiSelected.size());
				updateTitle(sc);
			}
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	private void rebake() {
		if (Debug.DEBUG) {
			Log.d(TAG, "rebake");
		}
		//
		mGvs.stop();
		mGvs.invalidate();

		if (mAllImages != null) {
			mAllImages.close();
			mAllImages = null;
		}
		// load
		initLoad();
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "done rebake");
		}
	}

	// private void prepareLoad() {
	// mGvs.stop();
	// if (mAllImages != null) {
	// mAllImages.close();
	// mAllImages = null;
	// }
	//
	// // if (mMediaScanningDialog != null) {
	// // mMediaScanningDialog.cancel();
	// // mMediaScanningDialog = null;
	// // }
	// //
	// // if (scanning) {
	// // mMediaScanningDialog = ProgressDialog.show(this, null,
	// // getResources().getString(R.string.wait), true, true);
	// // }
	// }

	// private void restoreState(Bundle b) {
	// if (Debug.DEBUG) {
	// Log.d(TAG, "restoreState: " + b);
	// }
	// if (mAllImages == null) {
	// mAllImages = new FileList();
	// }
	// mScrollPosition = b.getFloat(STATE_SCROLL_POSITION);
	// mSelectedIndex = b.getInt(STATE_SELECTED_INDEX);
	//
	// mAllImages.restoreState(b);
	// //
	// refreshView();
	// }

	private void setCustomTitle(String title) {
		final TextView tv = (TextView) findViewById(R.id.left_text);
		tv.setText(title);
	}

	private void showError(Throwable e) {
		Toast.makeText(this, "" + e.getMessage(), Toast.LENGTH_SHORT).show();
	}

	private void showFooter() {
		mFooterOrganizeView.setVisibility(View.VISIBLE);
		if (mFooterAppear == null) {
			mFooterAppear = AnimationUtils.loadAnimation(this,
					R.anim.footer_appear);
		}
		mFooterOrganizeView.startAnimation(mFooterAppear);
	}

	private void showItem(FileItem item) {
		try {
			Uri base = getIntent().getData();
			if (Debug.DEBUG) {
				Log.d(TAG, "base uri: " + base);
			}

			final Activity ctx = this;
			final String mimetype = "image/*";

			final Uri uri = Uri.parse("file:" + item.getName());

			if (Debug.DEBUG) {
				Log.d(TAG, "showItem: " + uri + " mimetype: " + mimetype);
			}

			Intent i = new Intent(Intent.ACTION_VIEW);
			i.setDataAndType(uri, mimetype);
			//
			i.putExtra("windowTitle", uri.getLastPathSegment());
			ctx.startActivityForResult(i, RC_VIEW);

			// ctx.startActivity(Intent.createChooser(i, uri
			// .getLastPathSegment()));
		} catch (Exception e) {
			showError(e);
		}
	}

	private void toggleMultiSelected(FileItem item) {
		int original = mMultiSelected.size();
		if (!mMultiSelected.add(item)) {
			mMultiSelected.remove(item);
		}
		mGvs.invalidate();

		if (original == 0) {
			btnOk.setEnabled(true);
		}

		int sc = mMultiSelected.size();
		if (sc == 0) {
			btnOk.setEnabled(false);
		}
		updateTitle(sc);
	}

	private void updateTitle(int sc) {
//		if (mAllImages == null) {
//			return;
//		}
//		int tc = mAllImages.getCount();
//		String t = (tc > 0 ? getString(R.string.title_multi_selected,
//				windowTitle, sc, tc) : windowTitle);
	}

	// private void toggleMultiSelected(IImage image) {
	// int original = mMultiSelected.size();
	// if (!mMultiSelected.add(image)) {
	// mMultiSelected.remove(image);
	// }
	// mGvs.invalidate();
	// if (original == 0) {
	// showFooter();
	// }
	// if (mMultiSelected.size() == 0) {
	// hideFooter();
	// }
	// }
}
